#include "CCircleQueueBuff.h"

CCircleQueueBuff::CCircleQueueBuff(int nMaxSize)
{
	m_pBuff = new BYTE[nMaxSize];
	m_nMaxBuffSize = 0;
	m_nHead = 0;
	m_nDataSize = 0;
	if(m_pBuff) {
		m_nMaxBuffSize = nMaxSize;
		Clean();
	}
}

CCircleQueueBuff::~CCircleQueueBuff()
{
	if(m_pBuff) {
		delete []m_pBuff;
		m_pBuff = NULL;
	}
}

//Clean up the buffer with FillByte
bool CCircleQueueBuff::Clean(BYTE nFillByte)
{
	if(m_pBuff == NULL)
		return false;

	memset(m_pBuff, nFillByte, m_nMaxBuffSize);
	m_nHead = 0;
	m_nDataSize = 0;
	return true;
}

//Get now date length
int CCircleQueueBuff::getAvailableDataLenght()
{
	return m_nDataSize;
}

//Get now empty space lenght.
int CCircleQueueBuff::getEmptySpaceLenght()
{
	return m_nMaxBuffSize - m_nDataSize;
}

//Write data to CCQBuff. Return written size.
//Return
//	-1 m_pBuff == NULL || pData == NULL
//	-2 No space for writing.
int CCircleQueueBuff:: WriteData(BYTE *pData, int nLen)
{
	if(m_pBuff == NULL || pData == NULL || nLen > m_nMaxBuffSize)
		return -1;
	
	//Check empty space enough.,
	int nEmptySpace = getEmptySpaceLenght();
	if(nEmptySpace == 0)
		return -2;

	int nWriteLen = (nLen > nEmptySpace)? nEmptySpace: nLen; //Checking can write data length.
	
	int nPartSize = 0;
	int nTail = m_nHead + m_nDataSize;	//End of data.
	if(nTail < m_nMaxBuffSize) {
		nPartSize = (nWriteLen > (m_nMaxBuffSize - nTail))? m_nMaxBuffSize - nTail: nWriteLen;
		memcpy(&m_pBuff[nTail], pData, nPartSize);
		nTail += nPartSize;
	}

	//need cycling?
	if(nPartSize != nWriteLen) {
		int nPart2Size = nWriteLen - nPartSize;
		int nWritePos = nTail % m_nMaxBuffSize;
		memcpy(&m_pBuff[nWritePos], &pData[nPartSize], nPart2Size);
	}

	m_nDataSize += nWriteLen;
	return nWriteLen;
}

//Write data to CCQBuff and promise all data will be storing.
//Return
//	-1 m_pBuff == NULL || pData == NULL
//	-2 Not enouhg space for writing.
int CCircleQueueBuff::WriteDataMakeAllSure(BYTE *pData, int nLen)
{
	if(m_pBuff == NULL || pData == NULL)
		return -1;
	if(nLen > getEmptySpaceLenght())
		return -2;
	
	return WriteData(pData, nLen);
}

//Read data from CCQBuff. Store the data to pBuf.
//Return
//	-1 m_pBuff == NULL || pBuf == NULL
//	-2 No date for reading.
int CCircleQueueBuff::ReadData(BYTE *pBuf, int nMaxLen)
{
	if(m_pBuff == NULL || pBuf == NULL)
		return -1;

	if(m_nDataSize == 0)
		return -2;
	
	int nReadLen = (nMaxLen > m_nDataSize)? m_nDataSize: nMaxLen; //Check max read data lenght.
	int nTail = m_nHead + m_nDataSize;	//End of data.
	int nPartSize = (nTail > m_nMaxBuffSize)? m_nMaxBuffSize - m_nHead: m_nDataSize;
	if(nReadLen < nPartSize)
		nPartSize = nReadLen;
	memcpy(pBuf, &m_pBuff[m_nHead], nPartSize);
	m_nHead += nPartSize;
	if(m_nHead >= m_nMaxBuffSize)
		m_nHead = 0;

	//need cycling?
	if(nPartSize != nReadLen) {
		int nPart2Size = nReadLen - nPartSize;
		memcpy(&pBuf[nPartSize], &m_pBuff[m_nHead], nPart2Size);
		m_nHead += nPart2Size;
	}

	m_nDataSize -= nReadLen;
	return nReadLen;
}

//Read data from CCQBuff and promise all data will be read.
//Return
//	-1 m_pBuff == NULL || pBuf == NULL
//	-2 not enouhg date for reading.
int CCircleQueueBuff::ReadDataMakeAllSure(BYTE *pBuf, int nMaxLen)
{
	if(m_pBuff == NULL || pBuf == NULL)
		return -1;
	if(nMaxLen > m_nDataSize)
		return -2;

	return ReadData(pBuf, nMaxLen);
}

//Get string from buffer with endSymbol.
string CCircleQueueBuff::GetString(char endSymbol)
{
	if(m_pBuff == NULL || m_nDataSize == 0)
		return std::string("NULL");

	string tmpString;
	int nTail = m_nHead + m_nDataSize;	//End of data.
	char *start = (char *)&m_pBuff[m_nHead];	//buf start point
	char *end = (char *)&m_pBuff[(nTail % m_nMaxBuffSize)];	//buf end point
	char *change = (char *)&m_pBuff[m_nMaxBuffSize-1];
	char *find = start;
	do {
		if(*find == endSymbol) {	//find string end.
			int nReadLen = 0;
			if(find > start) 
				nReadLen = find - start + 1;
			else
				nReadLen = (change - start + 1) + (find - (char *)m_pBuff + 1);
			m_nHead += nReadLen;
			m_nHead = m_nHead % m_nMaxBuffSize;
			m_nDataSize -= nReadLen;
			return tmpString;
		}
		tmpString.append(1,*find);	//appeding char.
		if(find == change)
			find = (char *)m_pBuff;
		else
			find++;
	}while(find != end);
	return string("NULL");
}